 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.ui.internal.ide.dialogs;

 import java.io.IOException ;
 import java.io.InputStream ;
 import java.net.URL ;
 import java.util.ArrayList ;
 import java.util.Iterator ;

 import javax.xml.parsers.ParserConfigurationException ;

 import org.eclipse.core.resources.IMarker;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.preference.JFacePreferences;
 import org.eclipse.jface.resource.JFaceColors;
 import org.eclipse.jface.resource.JFaceResources;
 import org.eclipse.jface.util.IPropertyChangeListener;
 import org.eclipse.jface.util.PropertyChangeEvent;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CLabel;
 import org.eclipse.swt.custom.ScrolledComposite;
 import org.eclipse.swt.custom.StyleRange;
 import org.eclipse.swt.custom.StyledText;
 import org.eclipse.swt.events.DisposeEvent;
 import org.eclipse.swt.events.DisposeListener;
 import org.eclipse.swt.events.FocusAdapter;
 import org.eclipse.swt.events.FocusEvent;
 import org.eclipse.swt.events.KeyEvent;
 import org.eclipse.swt.events.KeyListener;
 import org.eclipse.swt.events.MouseAdapter;
 import org.eclipse.swt.events.MouseEvent;
 import org.eclipse.swt.events.MouseMoveListener;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.events.TraverseEvent;
 import org.eclipse.swt.events.TraverseListener;
 import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.Cursor;
 import org.eclipse.swt.graphics.GC;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.ui.IEditorInput;
 import org.eclipse.ui.IEditorSite;
 import org.eclipse.ui.PartInitException;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages;
 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
 import org.eclipse.ui.internal.ide.IIDEHelpContextIds;
 import org.eclipse.ui.part.EditorPart;
 import org.xml.sax.SAXException ;

 /**
  * A "fake" editor to show a welcome page
  * The contents of this page are supplied in the product configuration
  *
  * PRIVATE
  * This class is internal to the workbench and must not be called outside the workbench
  */
 public class WelcomeEditor extends EditorPart {

     private final static int HORZ_SCROLL_INCREMENT = 20;

     private final static int VERT_SCROLL_INCREMENT = 20;

     // width at which wrapping will stop and a horizontal scroll bar will be
 // introduced
 private final static int WRAP_MIN_WIDTH = 150;

     private Composite editorComposite;

     private Cursor handCursor;

     private Cursor busyCursor;

     private WelcomeParser parser;

     private ArrayList hyperlinkRanges = new ArrayList ();

     private ArrayList texts = new ArrayList ();

     private ScrolledComposite scrolledComposite;

     private IPropertyChangeListener colorListener;

     private boolean mouseDown = false;

     private boolean dragEvent = false;

     private StyledText firstText, lastText;

     private StyledText lastNavigatedText, currentText;

     private boolean nextTabAbortTraversal, previousTabAbortTraversal = false;

     private WelcomeEditorCopyAction copyAction;

     /**
      * Create a new instance of the welcome editor
      */
     public WelcomeEditor() {
         super();
         setPartName(IDEWorkbenchMessages.WelcomeEditor_title);
         copyAction = new WelcomeEditorCopyAction(this);
         copyAction.setEnabled(false);
     }

     /**
      * Update the welcome page to start at the
      * beginning of the text.
      */
     private void focusOn(StyledText newText, int caretOffset) {
         if (newText == null) {
             return;
         }
         newText.setFocus();
         newText.setCaretOffset(caretOffset);
         scrolledComposite.setOrigin(0, newText.getLocation().y);
     }

     /**
      * Finds the next text
      */
     private StyledText nextText(StyledText text) {
         int index = 0;
         if (text == null) {
             return (StyledText) texts.get(0);
         } else {
             index = texts.indexOf(text);
         }

         //If we are not at the end....
 if (index < texts.size() - 1) {
             return (StyledText) texts.get(index + 1);
         } else {
             return (StyledText) texts.get(0);
         }
     }

     /**
      * Finds the previous text
      */
     private StyledText previousText(StyledText text) {
         int index = 0;
         if (text == null) {
             return (StyledText) texts.get(0);
         } else {
             index = texts.indexOf(text);
         }

         //If we are at the beginning....
 if (index == 0) {
             return (StyledText) texts.get(texts.size() - 1);
         } else {
             return (StyledText) texts.get(index - 1);
         }
     }

     /**
      * Returns the current text.
      */
     protected StyledText getCurrentText() {
         return currentText;
     }

     /**
      * Returns the copy action.
      */
     protected WelcomeEditorCopyAction getCopyAction() {
         return copyAction;
     }

     /**
      * Finds the next link after the current selection.
      */
     private StyleRange findNextLink(StyledText text) {
         if (text == null) {
             return null;
         }

         WelcomeItem item = (WelcomeItem) text.getData();
         StyleRange[] ranges = text.getStyleRanges();
         int currentSelectionEnd = text.getSelection().y;

         for (int i = 0; i < ranges.length; i++) {
             if (ranges[i].start >= currentSelectionEnd) {
                 if (item.isLinkAt(ranges[i].start)) {
                     return ranges[i];
                 }
             }
         }
         return null;
     }

     /**
      * Finds the previous link before the current selection.
      */
     private StyleRange findPreviousLink(StyledText text) {
         if (text == null) {
             return null;
         }

         WelcomeItem item = (WelcomeItem) text.getData();
         StyleRange[] ranges = text.getStyleRanges();
         int currentSelectionStart = text.getSelection().x;

         for (int i = ranges.length - 1; i > -1; i--) {
             if ((ranges[i].start + ranges[i].length) < currentSelectionStart) {
                 if (item.isLinkAt(ranges[i].start + ranges[i].length - 1)) {
                     return ranges[i];
                 }
             }
         }
         return null;
     }

     /**
      * Finds the current link of the current selection.
      */
     protected StyleRange getCurrentLink(StyledText text) {
         StyleRange[] ranges = text.getStyleRanges();
         int currentSelectionEnd = text.getSelection().y;
         int currentSelectionStart = text.getSelection().x;

         for (int i = 0; i < ranges.length; i++) {
             if ((currentSelectionStart >= ranges[i].start)
                     && (currentSelectionEnd <= (ranges[i].start + ranges[i].length))) {
                 return ranges[i];
             }
         }
         return null;
     }

     /**
      * Adds listeners to the given styled text
      */
     private void addListeners(StyledText styledText) {
         styledText.addMouseListener(new MouseAdapter() {
             public void mouseDown(MouseEvent e) {
                 if (e.button != 1) {
                     return;
                 }
                 mouseDown = true;
             }

             public void mouseUp(MouseEvent e) {
                 mouseDown = false;
                 StyledText text = (StyledText) e.widget;
                 WelcomeItem item = (WelcomeItem) e.widget.getData();
                 int offset = text.getCaretOffset();
                 if (dragEvent) {
                     dragEvent = false;
                     if (item.isLinkAt(offset)) {
                         text.setCursor(handCursor);
                     }
                 } else if (item.isLinkAt(offset)) {
                     text.setCursor(busyCursor);
                     if (e.button == 1) {
                         item.triggerLinkAt(offset);
                         StyleRange selectionRange = getCurrentLink(text);
                         text.setSelectionRange(selectionRange.start,
                                 selectionRange.length);
                         text.setCursor(null);
                     }
                 }
             }
         });

         styledText.addMouseMoveListener(new MouseMoveListener() {
             public void mouseMove(MouseEvent e) {
                 // Do not change cursor on drag events
 if (mouseDown) {
                     if (!dragEvent) {
                         StyledText text = (StyledText) e.widget;
                         text.setCursor(null);
                     }
                     dragEvent = true;
                     return;
                 }
                 StyledText text = (StyledText) e.widget;
                 WelcomeItem item = (WelcomeItem) e.widget.getData();
                 int offset = -1;
                 try {
                     offset = text.getOffsetAtLocation(new Point(e.x, e.y));
                 } catch (IllegalArgumentException ex) {
                     // location is not over a character
 }
                 if (offset == -1) {
                     text.setCursor(null);
                 } else if (item.isLinkAt(offset)) {
                     text.setCursor(handCursor);
                 } else {
                     text.setCursor(null);
                 }
             }
         });

         styledText.addTraverseListener(new TraverseListener() {
             public void keyTraversed(TraverseEvent e) {
                 StyledText text = (StyledText) e.widget;

                 switch (e.detail) {
                 case SWT.TRAVERSE_ESCAPE:
                     e.doit = true;
                     break;
                 case SWT.TRAVERSE_TAB_NEXT:
                     // Handle Ctrl-Tab
 if ((e.stateMask & SWT.CTRL) != 0) {
                         if (e.widget == lastText) {
                             return;
                         } else {
                             e.doit = false;
                             nextTabAbortTraversal = true;
                             lastText.traverse(SWT.TRAVERSE_TAB_NEXT);
                             return;
                         }
                     }
                     if (nextTabAbortTraversal) {
                         nextTabAbortTraversal = false;
                         return;
                     }
                     // Find the next link in current widget, if applicable
 // Stop at top of widget
 StyleRange nextLink = findNextLink(text);
                     if (nextLink == null) {
                         // go to the next widget, focus at beginning
 StyledText nextText = nextText(text);
                         nextText.setSelection(0);
                         focusOn(nextText, 0);
                     } else {
                         // focusOn: allow none tab traversals to align
 focusOn(text, text.getSelection().x);
                         text.setSelectionRange(nextLink.start, nextLink.length);
                     }
                     e.detail = SWT.TRAVERSE_NONE;
                     e.doit = true;
                     break;
                 case SWT.TRAVERSE_TAB_PREVIOUS:
                     // Handle Ctrl-Shift-Tab
 if ((e.stateMask & SWT.CTRL) != 0) {
                         if (e.widget == firstText) {
                             return;
                         } else {
                             e.doit = false;
                             previousTabAbortTraversal = true;
                             firstText.traverse(SWT.TRAVERSE_TAB_PREVIOUS);
                             return;
                         }
                     }
                     if (previousTabAbortTraversal) {
                         previousTabAbortTraversal = false;
                         return;
                     }
                     // Find the previous link in current widget, if applicable
 // Stop at top of widget also
 StyleRange previousLink = findPreviousLink(text);
                     if (previousLink == null) {
                         if (text.getSelection().x == 0) {
                             // go to the previous widget, focus at end
 StyledText previousText = previousText(text);
                             previousText.setSelection(previousText
                                     .getCharCount());
                             previousLink = findPreviousLink(previousText);
                             if (previousLink == null) {
                                 focusOn(previousText, 0);
                             } else {
                                 focusOn(previousText, previousText
                                         .getSelection().x);
                                 previousText
                                         .setSelectionRange(previousLink.start,
                                                 previousLink.length);
                             }
                         } else {
                             // stay at top of this widget
 focusOn(text, 0);
                         }
                     } else {
                         // focusOn: allow none tab traversals to align
 focusOn(text, text.getSelection().x);
                         text.setSelectionRange(previousLink.start,
                                 previousLink.length);
                     }
                     e.detail = SWT.TRAVERSE_NONE;
                     e.doit = true;
                     break;
                 default:
                     break;
                 }
             }
         });

         styledText.addKeyListener(new KeyListener() {
             public void keyReleased(KeyEvent e) {
                 //Ignore a key release
 }

             public void keyPressed(KeyEvent event) {
                 StyledText text = (StyledText) event.widget;
                 if (event.character == ' ' || event.character == SWT.CR) {
                     if (text != null) {
                         WelcomeItem item = (WelcomeItem) text.getData();

                         //Be sure we are in the selection
 int offset = text.getSelection().x + 1;

                         if (item.isLinkAt(offset)) {
                             text.setCursor(busyCursor);
                             item.triggerLinkAt(offset);
                             StyleRange selectionRange = getCurrentLink(text);
                             text.setSelectionRange(selectionRange.start,
                                     selectionRange.length);
                             text.setCursor(null);
                         }
                     }
                     return;
                 }

                 // When page down is pressed, move the cursor to the next item in the
 // welcome page. Note that this operation wraps (pages to the top item
 // when the last item is reached).
 if (event.keyCode == SWT.PAGE_DOWN) {
                     focusOn(nextText(text), 0);
                     return;
                 }

                 // When page up is pressed, move the cursor to the previous item in the
 // welcome page. Note that this operation wraps (pages to the bottom item
 // when the first item is reached).
 if (event.keyCode == SWT.PAGE_UP) {
                     focusOn(previousText(text), 0);
                     return;
                 }
             }
         });

         styledText.addFocusListener(new FocusAdapter() {
             public void focusLost(FocusEvent e) {
                 // Remember current text widget
 lastNavigatedText = (StyledText) e.widget;
             }

             public void focusGained(FocusEvent e) {
                 currentText = (StyledText) e.widget;

                 // Remove highlighted selection if text widget has changed
 if ((currentText != lastNavigatedText)
                         && (lastNavigatedText != null)) {
                     lastNavigatedText.setSelection(lastNavigatedText
                             .getSelection().x);
                 }

                 // enable/disable copy action
 copyAction.setEnabled(currentText.getSelectionCount() > 0);
             }
         });

         styledText.addSelectionListener(new SelectionAdapter() {
             public void widgetSelected(SelectionEvent e) {
                 // enable/disable copy action
 StyledText text = (StyledText) e.widget;
                 copyAction.setEnabled(text.getSelectionCount() > 0);
             }
         });
     }

     /**
      * Creates the wizard's title area.
      *
      * @param parent the SWT parent for the title area composite
      * @return the created info area composite
      */
     private Composite createInfoArea(Composite parent) {
         // Create the title area which will contain
 // a title, message, and image.
 this.scrolledComposite = new ScrolledComposite(parent, SWT.V_SCROLL
                 | SWT.H_SCROLL);
         this.scrolledComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
         final Composite infoArea = new Composite(this.scrolledComposite,
                 SWT.NONE);
         GridLayout layout = new GridLayout();
         layout.marginHeight = 10;
         layout.verticalSpacing = 5;
         layout.numColumns = 2;
         infoArea.setLayout(layout);
         GridData data = new GridData(GridData.FILL_BOTH);
         infoArea.setLayoutData(data);
         boolean wrapped = parser.isFormatWrapped();
         int HINDENT = 20;

         // Get the background color for the title area
 Display display = parent.getDisplay();
         Color background = JFaceColors.getBannerBackground(display);
         Color foreground = JFaceColors.getBannerForeground(display);
         infoArea.setBackground(background);

         int textStyle = SWT.MULTI | SWT.READ_ONLY;
         if (wrapped) {
             textStyle = textStyle | SWT.WRAP;
         }
         StyledText sampleStyledText = null;
         // Create the intro item
 WelcomeItem item = getIntroItem();
         if (item != null) {
             StyledText styledText = new StyledText(infoArea, textStyle);
             this.texts.add(styledText);
             sampleStyledText = styledText;
             styledText.setCursor(null);
             JFaceColors.setColors(styledText, foreground, background);
             styledText.setText(getIntroItem().getText());
             setBoldRanges(styledText, item.getBoldRanges());
             setLinkRanges(styledText, item.getActionRanges());
             setLinkRanges(styledText, item.getHelpRanges());
             GridData gd = new GridData(GridData.FILL_HORIZONTAL);
             gd.horizontalSpan = 2;
             gd.horizontalIndent = HINDENT;
             gd.verticalAlignment = GridData.VERTICAL_ALIGN_BEGINNING;
             styledText.setLayoutData(gd);
             styledText.setData(item);
             addListeners(styledText);

             Label spacer = new Label(infoArea, SWT.NONE);
             spacer.setBackground(background);
             gd = new GridData(GridData.VERTICAL_ALIGN_BEGINNING);
             gd.horizontalSpan = 2;
             spacer.setLayoutData(gd);
         }
         firstText = sampleStyledText;

         // Create the welcome items
 Label imageLabel = null;
         WelcomeItem[] items = getItems();
         for (int i = 0; i < items.length; i++) {
             Label label = new Label(infoArea, SWT.NONE);
             label.setBackground(background);
             label
                     .setImage(PlatformUI
                             .getWorkbench()
                             .getSharedImages()
                             .getImage(
                                     IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_ITEM));
             GridData gd = new GridData();
             gd.horizontalIndent = HINDENT;
             gd.verticalAlignment = GridData.VERTICAL_ALIGN_BEGINNING;
             label.setLayoutData(gd);
             if (imageLabel == null) {
                 imageLabel = label;
             }

             StyledText styledText = new StyledText(infoArea, textStyle);
             this.texts.add(styledText);
             sampleStyledText = styledText;
             styledText.setCursor(null);
             JFaceColors.setColors(styledText, foreground, background);
             styledText.setText(items[i].getText());
             setBoldRanges(styledText, items[i].getBoldRanges());
             setLinkRanges(styledText, items[i].getActionRanges());
             setLinkRanges(styledText, items[i].getHelpRanges());
             gd = new GridData(GridData.FILL_HORIZONTAL);
             gd.verticalAlignment = GridData.VERTICAL_ALIGN_BEGINNING;
             gd.verticalSpan = 2;
             styledText.setLayoutData(gd);
             styledText.setData(items[i]);
             addListeners(styledText);

             Label spacer = new Label(infoArea, SWT.NONE);
             spacer.setBackground(background);
             gd = new GridData(GridData.VERTICAL_ALIGN_BEGINNING);
             gd.horizontalSpan = 2;
             spacer.setLayoutData(gd);

             // create context menu
 MenuManager menuMgr = new MenuManager("#PopUp"); //$NON-NLS-1$
 menuMgr.add(copyAction);
             styledText.setMenu(menuMgr.createContextMenu(styledText));
         }

         lastText = sampleStyledText;
         this.scrolledComposite.setContent(infoArea);
         Point p = infoArea.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
         this.scrolledComposite.setMinHeight(p.y);
         if (wrapped) {
             // introduce a horizontal scroll bar after a minimum width is reached
 this.scrolledComposite.setMinWidth(WRAP_MIN_WIDTH);
         } else {
             this.scrolledComposite.setMinWidth(p.x);
         }
         this.scrolledComposite.setExpandHorizontal(true);
         this.scrolledComposite.setExpandVertical(true);

         // When the welcome editor is resized, we need to set the width hint for
 // wrapped StyledText widgets so that the wrapped height will be recalculated.
 if (wrapped && (imageLabel != null)) {
             // figure out how wide the StyledText widgets should be, do this by first
 // calculating the width of the area not used by styled text widgets
 Rectangle bounds = imageLabel.getBounds();
             final int adjust = HINDENT + bounds.width + layout.verticalSpacing
                     + (layout.marginWidth * 2);
             final int adjustFirst = HINDENT + (layout.marginWidth * 2);
             infoArea.addListener(SWT.Resize, new Listener() {
                 public void handleEvent(Event event) {
                     int w = scrolledComposite.getClientArea().width;
                     // if the horizontal scroll bar exists, we want to wrap to the
 // minimum wrap width
 if (w < WRAP_MIN_WIDTH) {
                         w = WRAP_MIN_WIDTH;
                     }
                     for (int i = 0; i < texts.size(); i++) {
                         int extent;
                         if (i == 0) {
                             extent = w - adjustFirst;
                         } else {
                             extent = w - adjust;
                         }
                         StyledText text = (StyledText) texts.get(i);
                         Point p = text.computeSize(extent, SWT.DEFAULT, false);
                         ((GridData) text.getLayoutData()).widthHint = p.x;
                     }
                     // reset the scrolled composite height since the height of the
 // styled text widgets have changed
 Point p = infoArea.computeSize(SWT.DEFAULT, SWT.DEFAULT,
                             true);
                     scrolledComposite.setMinHeight(p.y);
                 }
             });
         }

         // Adjust the scrollbar increments
 if (sampleStyledText == null) {
             this.scrolledComposite.getHorizontalBar().setIncrement(
                     HORZ_SCROLL_INCREMENT);
             this.scrolledComposite.getVerticalBar().setIncrement(
                     VERT_SCROLL_INCREMENT);
         } else {
             GC gc = new GC(sampleStyledText);
             int width = gc.getFontMetrics().getAverageCharWidth();
             gc.dispose();
             this.scrolledComposite.getHorizontalBar().setIncrement(width);
             this.scrolledComposite.getVerticalBar().setIncrement(
                     sampleStyledText.getLineHeight());
         }
         return infoArea;
     }

     /**
      * Creates the SWT controls for this workbench part.
      * <p>
      * Clients should not call this method (the workbench calls this method at
      * appropriate times).
      * </p>
      * <p>
      * For implementors this is a multi-step process:
      * <ol>
      * <li>Create one or more controls within the parent.</li>
      * <li>Set the parent layout as needed.</li>
      * <li>Register any global actions with the <code>IActionService</code>.</li>
      * <li>Register any popup menus with the <code>IActionService</code>.</li>
      * <li>Register a selection provider with the <code>ISelectionService</code>
      * (optional). </li>
      * </ol>
      * </p>
      *
      * @param parent the parent control
      */
     public void createPartControl(Composite parent) {
         // read our contents
 readFile();
         if (parser == null) {
             return;
         }

         handCursor = new Cursor(parent.getDisplay(), SWT.CURSOR_HAND);
         busyCursor = new Cursor(parent.getDisplay(), SWT.CURSOR_WAIT);

         editorComposite = new Composite(parent, SWT.NONE);
         GridLayout layout = new GridLayout();
         layout.marginHeight = 0;
         layout.marginWidth = 0;
         layout.verticalSpacing = 0;
         layout.horizontalSpacing = 0;
         editorComposite.setLayout(layout);

         createTitleArea(editorComposite);

         Label titleBarSeparator = new Label(editorComposite, SWT.HORIZONTAL
                 | SWT.SEPARATOR);
         GridData gd = new GridData(GridData.FILL_HORIZONTAL);
         titleBarSeparator.setLayoutData(gd);

         createInfoArea(editorComposite);

         getSite().getWorkbenchWindow().getWorkbench().getHelpSystem().setHelp(
                 editorComposite, IIDEHelpContextIds.WELCOME_EDITOR);

         this.colorListener = new IPropertyChangeListener() {
             public void propertyChange(PropertyChangeEvent event) {
                 if (event.getProperty()
                         .equals(JFacePreferences.HYPERLINK_COLOR)) {
                     Color fg = JFaceColors.getHyperlinkText(editorComposite
                             .getDisplay());
                     Iterator links = hyperlinkRanges.iterator();
                     while (links.hasNext()) {
                         StyleRange range = (StyleRange) links.next();
                         range.foreground = fg;
                     }
                 }
             }
         };

         JFacePreferences.getPreferenceStore().addPropertyChangeListener(
                 this.colorListener);

     }

     /**
      * Creates the wizard's title area.
      *
      * @param parent the SWT parent for the title area composite
      * @return the created title area composite
      */
     private Composite createTitleArea(Composite parent) {
         // Get the background color for the title area
 Display display = parent.getDisplay();
         Color background = JFaceColors.getBannerBackground(display);
         Color foreground = JFaceColors.getBannerForeground(display);

         // Create the title area which will contain
 // a title, message, and image.
 Composite titleArea = new Composite(parent, SWT.NONE | SWT.NO_FOCUS);
         GridLayout layout = new GridLayout();
         layout.marginHeight = 0;
         layout.marginWidth = 0;
         layout.verticalSpacing = 0;
         layout.horizontalSpacing = 0;
         layout.numColumns = 2;
         titleArea.setLayout(layout);
         titleArea.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
         titleArea.setBackground(background);

         // Message label
 final CLabel messageLabel = new CLabel(titleArea, SWT.LEFT) {
             protected String shortenText(GC gc, String text, int width) {
                 if (gc.textExtent(text, SWT.DRAW_MNEMONIC).x <= width) {
                     return text;
                 }
                 final String ellipsis = "..."; //$NON-NLS-1$
 int ellipseWidth = gc.textExtent(ellipsis, SWT.DRAW_MNEMONIC).x;
                 int length = text.length();
                 int end = length - 1;
                 while (end > 0) {
                     text = text.substring(0, end);
                     int l1 = gc.textExtent(text, SWT.DRAW_MNEMONIC).x;
                     if (l1 + ellipseWidth <= width) {
                         return text + ellipsis;
                     }
                     end--;
                 }
                 return text + ellipsis;
             }
         };
         JFaceColors.setColors(messageLabel, foreground, background);
         messageLabel.setText(getBannerTitle());
         messageLabel.setFont(JFaceResources.getHeaderFont());

         final IPropertyChangeListener fontListener = new IPropertyChangeListener() {
             public void propertyChange(PropertyChangeEvent event) {
                 if (JFaceResources.HEADER_FONT.equals(event.getProperty())) {
                     messageLabel.setFont(JFaceResources.getHeaderFont());
                 }
             }
         };

         messageLabel.addDisposeListener(new DisposeListener() {
             public void widgetDisposed(DisposeEvent event) {
                 JFaceResources.getFontRegistry().removeListener(fontListener);
             }
         });

         JFaceResources.getFontRegistry().addListener(fontListener);

         GridData gd = new GridData(GridData.FILL_BOTH);
         messageLabel.setLayoutData(gd);

         // Title image
 Label titleImage = new Label(titleArea, SWT.LEFT);
         titleImage.setBackground(background);
         titleImage.setImage(PlatformUI.getWorkbench().getSharedImages()
                 .getImage(IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_BANNER));
         gd = new GridData();
         gd.horizontalAlignment = GridData.END;
         titleImage.setLayoutData(gd);

         return titleArea;
     }

     /**
      * The <code>WorkbenchPart</code> implementation of this
      * <code>IWorkbenchPart</code> method disposes the title image
      * loaded by <code>setInitializationData</code>. Subclasses may extend.
      */
     public void dispose() {
         super.dispose();
         if (busyCursor != null) {
             busyCursor.dispose();
         }
         if (handCursor != null) {
             handCursor.dispose();
         }
         if (this.colorListener != null) {
             JFacePreferences.getPreferenceStore().removePropertyChangeListener(
                     this.colorListener);
         }
     }

     /* (non-Javadoc)
      * Saves the contents of this editor.
      * <p>
      * Subclasses must override this method to implement the open-save-close lifecycle
      * for an editor. For greater details, see <code>IEditorPart</code>
      * </p>
      *
      * @see IEditorPart
      */
     public void doSave(IProgressMonitor monitor) {
         // do nothing
 }

     /* (non-Javadoc)
      * Saves the contents of this editor to another object.
      * <p>
      * Subclasses must override this method to implement the open-save-close lifecycle
      * for an editor. For greater details, see <code>IEditorPart</code>
      * </p>
      *
      * @see IEditorPart
      */
     public void doSaveAs() {
         // do nothing
 }

     /**
      * Returns the title obtained from the parser
      */
     private String getBannerTitle() {
         if (parser.getTitle() == null) {
             return ""; //$NON-NLS-1$
 }
         return parser.getTitle();
     }

     /**
      * Returns the intro item or <code>null</code>
      */
     private WelcomeItem getIntroItem() {
         return parser.getIntroItem();
     }

     /**
      * Returns the welcome items
      */
     private WelcomeItem[] getItems() {
         return parser.getItems();
     }

     /* (non-Javadoc)
      * Sets the cursor and selection state for this editor to the passage defined
      * by the given marker.
      * <p>
      * Subclasses may override. For greater details, see <code>IEditorPart</code>
      * </p>
      *
      * @see IEditorPart
      */
     public void gotoMarker(IMarker marker) {
         // do nothing
 }

     /* (non-Javadoc)
      * Initializes the editor part with a site and input.
      * <p>
      * Subclasses of <code>EditorPart</code> must implement this method. Within
      * the implementation subclasses should verify that the input type is acceptable
      * and then save the site and input. Here is sample code:
      * </p>
      * <pre>
      * if (!(input instanceof IFileEditorInput))
      * throw new PartInitException("Invalid Input: Must be IFileEditorInput");
      * setSite(site);
      * setInput(editorInput);
      * </pre>
      */
     public void init(IEditorSite site, IEditorInput input)
             throws PartInitException {
         if (!(input instanceof WelcomeEditorInput)) {
             throw new PartInitException(
                     "Invalid Input: Must be WelcomeEditorInput"); //$NON-NLS-1$
 }
         setSite(site);
         setInput(input);
     }

     /* (non-Javadoc)
      * Returns whether the contents of this editor have changed since the last save
      * operation.
      * <p>
      * Subclasses must override this method to implement the open-save-close lifecycle
      * for an editor. For greater details, see <code>IEditorPart</code>
      * </p>
      *
      * @see IEditorPart
      */
     public boolean isDirty() {
         return false;
     }

     /* (non-Javadoc)
      * Returns whether the "save as" operation is supported by this editor.
      * <p>
      * Subclasses must override this method to implement the open-save-close lifecycle
      * for an editor. For greater details, see <code>IEditorPart</code>
      * </p>
      *
      * @see IEditorPart
      */
     public boolean isSaveAsAllowed() {
         return false;
     }

     /**
      * Read the contents of the welcome page
      *
      * @param is the <code>InputStream</code> to parse
      * @throws IOException if there is a problem parsing the stream.
      */
     public void read(InputStream is) throws IOException {
         try {
             parser = new WelcomeParser();
         } catch (ParserConfigurationException e) {
             throw (IOException ) (new IOException ().initCause(e));
         } catch (SAXException e) {
             throw (IOException ) (new IOException ().initCause(e));
         }
         parser.parse(is);
     }

     /**
      * Reads the welcome file
      */
     public void readFile() {
         URL url = ((WelcomeEditorInput) getEditorInput()).getAboutInfo()
                 .getWelcomePageURL();

         if (url == null) {
             // should not happen
 return;
         }

         InputStream is = null;
         try {
             is = url.openStream();
             read(is);
         } catch (IOException e) {
             IStatus status = new Status(IStatus.ERROR,
                     IDEWorkbenchPlugin.IDE_WORKBENCH, 1, IDEWorkbenchMessages.WelcomeEditor_accessException, e);
             IDEWorkbenchPlugin.log(IDEWorkbenchMessages.WelcomeEditor_readFileError, status);
         } finally {
             try {
                 if (is != null) {
                     is.close();
                 }
             } catch (IOException e) {
             }
         }
     }

     /**
      * Sets the styled text's bold ranges
      */
     private void setBoldRanges(StyledText styledText, int[][] boldRanges) {
         for (int i = 0; i < boldRanges.length; i++) {
            StyleRange r = new StyleRange(boldRanges[i][0], boldRanges[i][1],
                    null, null, SWT.BOLD);
            styledText.setStyleRange(r);
        }
    }

    /**
     * Asks this part to take focus within the workbench.
     * <p>
     * Clients should not call this method (the workbench calls this method at
     * appropriate times).
     * </p>
     */
    public void setFocus() {
        if ((editorComposite != null) && (lastNavigatedText == null)
                && (currentText == null)) {
            editorComposite.setFocus();
        }
    }

    /**
     * Sets the styled text's link (blue) ranges
     */
    private void setLinkRanges(StyledText styledText, int[][] linkRanges) {
        //Color fg = styledText.getDisplay().getSystemColor(SWT.COLOR_BLUE);
 Color fg = JFaceColors.getHyperlinkText(styledText.getShell()
                .getDisplay());
        for (int i = 0; i < linkRanges.length; i++) {
            StyleRange r = new StyleRange(linkRanges[i][0], linkRanges[i][1],
                    fg, null);
            styledText.setStyleRange(r);
            hyperlinkRanges.add(r);
        }
    }

}

